//
// Created by 抑~风 on 2022/12/9.
//

#include "semantic_analysis.h"
#include "../regexp.h"
#include<cassert>

namespace CWJ{
    namespace SEMA_ANALYSIS{

        FunctionType FunctionType::PRINT("print",nullptr,nullptr);
        FunctionType FunctionType::PRINTLN("println",nullptr,nullptr);

        Symbol Symbol::BREAK_SYMBOL(SymbolEnum::BREAK,"BREAK", nullptr, nullptr);
        Symbol Symbol::CONTINUE_SYMBOL(SymbolEnum::CONTINUE,"CONTINUE", nullptr, nullptr);


        int Literal::uid = 0;
        const string Literal::prefix = "Literal";

        CWJType CWJType::voidType("void",nullptr, nullptr,TypeEnum::VOID ,true);
        CWJType CWJType::intType("int",nullptr, nullptr,TypeEnum::INT ,true);
        CWJType CWJType::floatType("float",nullptr, nullptr,TypeEnum::FLOAT ,true);
        CWJType CWJType::stringType("string",nullptr, nullptr,TypeEnum::STRING ,true);

        const string &Symbol::getName() const {
            return name;
        }

        Scope* Symbol::getEnclosingScope() const {
            return enclosingScope;
        }

        const ResASTNode *Symbol::getCtx() const {
            return ctx;
        }

        void Symbol::setName(const string &name) {
            Symbol::name = name;
        }

        void Symbol::setEnclosingScope(Scope *enclosingScope) {
            Symbol::enclosingScope = enclosingScope;
        }

        void Symbol::setCtx(ResASTNodePtr ctx) {
            Symbol::ctx = ctx;
        }

        Symbol::Symbol(const SymbolEnum symbolEnum,
                       const string &name,
                       Scope *enclosingScope,
                       const ResASTNodePtr ctx)
                : symbolEnum(symbolEnum), name(name), enclosingScope(enclosingScope), ctx(ctx) {}

        const SymbolEnum Symbol::getSymbolEnum() const {
            return symbolEnum;
        }

        void Scope::addSymbol(Symbol *symbol) {
            this->symbols.push_back(symbol);
            symbol->setEnclosingScope(this);
        }

        void Scope::setParentScope(Scope *parentScope) {
            Scope::parentScope = parentScope;
        }

        Scope *Scope::getParentScope() const {
            return parentScope;
        }

        void Scope::addSubScope(Scope *scope) {
            this->subScopes.push_back(scope);
        }

        Scope::Scope(const ResASTNodePtr ctx, Scope *parentScope): ctx(ctx),parentScope(parentScope) {}

        const vector<Symbol *> &Scope::getSymbols() const {
            return symbols;
        }

        ScopeType Scope::getType() const {
            return type;
        }

        const vector<Scope *> &Scope::getSubScopes() const {
            return subScopes;
        }

        bool CWJType::isType(CWJType type) {
            return false;
        }

        bool CWJType::isPrimaryType() {
            return this->isPrimary;
        }

        CWJType::CWJType(
                   const string &name,
                   Scope *enclosingScope,
                   const ResASTNodePtr ctx,
                   const TypeEnum uid,
                   bool isPrimary) : Symbol(SymbolEnum::TYPE, name, enclosingScope, ctx), uid(uid),
                                                         isPrimary(isPrimary) {}

        const TypeEnum CWJType::getUid() const {
            return uid;
        }

        bool CWJType::isPrimary1() const {
            return isPrimary;
        }

        FunctionType::FunctionType(const string &name, Scope *scope, const ResASTNodePtr ctx) : CWJType(
                name,scope,ctx,TypeEnum::FUNCTION){}

        const CwjAny &FunctionType::getObject() const {
            return object;
        }

        void FunctionType::setObject(const CwjAny &object) {
            FunctionType::object = object;
        }

        const FunctionType::ResASTNodePtr FunctionType::getNode() const {
            return this->ctx;
        }

        void FunctionType::setNode(const ResASTNodePtr node) {
            this->ctx = node;
        }

        CWJType *FunctionType::getReturnTye() const {
            return returnTye;
        }

        void FunctionType::setReturnTye(CWJType *returnTye) {
            FunctionType::returnTye = returnTye;
        }

        void FunctionType::addParamType(CWJType *t) {
            this->paramTypes.push_back(t);
        }

        FunctionType::FunctionType(Scope *scope,
                                   CWJType::ResASTNodePtr const ctx)
                                   :
                                   CWJType("",scope,ctx,TypeEnum::FUNCTION){

            if(ctx){
                // 获取函数名
                this->name = ctx->getChilds()[1]->getVal();
            }else{
                throw runtime_error(" FunctionType::FunctionType error : funtion dec error");
            }

        }

        void FunctionType::addParam(Variable * v) {
            this->paramsVars.push_back(v);
        }

        bool FunctionType::match(const vector<CWJType *>& list) {
            if(list.size() != this->paramTypes.size())  return false;
            int len = list.size();
            for(int i=0;i<len;i++){
                if(list[i] != this->paramTypes[i])  return false;
            }
            return true;
        }

        const vector<CWJType *> &FunctionType::getParamTypes() const {
            return paramTypes;
        }

        const vector<Variable *> &FunctionType::getParamsVars() const {
            return paramsVars;
        }

        CWJType::ResASTNodePtr FunctionType::getBody() const {
            return body;
        }

        void FunctionType::setBody(ResASTNodePtr body) {
            FunctionType::body = body;
        }


        Symbol *AnnotatedTree::getSymbolOfNode(ResASTNodePtr ptr) {
            return this->symbolOfNodes[ptr];
        }

        Scope *AnnotatedTree::getScopeOfNode(AnnotatedTree::ResASTNodePtr ptr) {
            return this->scopeOfNodes[ptr];
        }

        const AnnotatedTree::ResASTNodePtr AnnotatedTree::getTree() const {
            return tree;
        }

        ostream &operator<<(ostream &os, const AnnotatedTree &tree) {
            cout<<"tree.ScopeOfNodes : ";
            for(const auto & c:tree.scopeOfNodes){
                cout<<c.first->getContext()<<" ";
            }
            cout<<endl;
            cout<<"tree.symbolOfNodes : ";
            for(const auto & c : tree.symbolOfNodes){
                cout<<c.first->getContext()<<" ";
            }
            cout<<endl;

            return os;
        }

        void AnnotatedTree::setTree(const ResASTNodePtr tree) {
            AnnotatedTree::tree = tree;
        }

        void AnnotatedTree::addSymbolOfNode(AnnotatedTree::ResASTNodePtr ctx, Symbol * symbol) {
            this->symbolOfNodes[ctx] = symbol;
        }

        void AnnotatedTree::addScopeOfNode(AnnotatedTree::ResASTNodePtr ctx , Scope * scope) {
//            cout<<"AnnotatedTree::addScopeOfNode:"<<ctx->getVal()<<" "<<scope<<endl;
            this->scopeOfNodes[ctx] = scope;
        }

        CWJType *AnnotatedTree::lookupClass(AnnotatedTree::ResASTNodePtr ctx, string name) {
//            cout<<"AnnotatedTree::lookupClass :"<<ctx->getContext()<<endl;
            auto& scope = this->scopeOfNodes[ctx];
            while(scope) {
//                cout<<"---"<<endl;
                for (Symbol *t : scope->getSymbols()) {

                    if (t->getSymbolEnum() == SymbolEnum::TYPE &&((CWJType *) t)->getUid() == TypeEnum::CLASS) {
                        CWJType* tmp = (CWJType *)t;

                        if(tmp->getName() == name) {
                            return tmp;
                        }
                    }
                }
                scope = scope->getParentScope();
            }
            return nullptr;
        }

        void AnnotatedTree::showScopeTree() {
            Scope * scope = this->getScopeOfNode(this->getTree());
            this->showScopeTree(scope,"");
        }

        void AnnotatedTree::showScopeTree(Scope* s,string text){
            cout<<(int)s->getType()<<endl;
            cout<<" s->getSymbols() :";
            for(auto o : s->getSymbols()){
                cout<<o->getName()<<" ";
            }
            cout<<endl;

            for(auto o : s->getSubScopes()){
                this->showScopeTree(o,text+"    ");
            }

        }

        CWJType* AnnotatedTree::getType(ResASTNodePtr node){
            if(node->getNodeType() == NodeType::TOKEN){
                const string& val = node->getContext();
                if(val == "INT")    return &CWJType::intType;
                else if(val == "VOID")  return &CWJType::voidType;
                else if(val == "IDENTIFIER")   {

                    auto v = this->lookupClass(node,node->getVal());
//                    if(v){
                        cout<<"TypeResolver::getType: "<<node->getVal()<<endl;
//                    }
                    return v;
                }
            }
            CWJType* type = nullptr;
            Symbol* sym = nullptr;
            if((sym=this->getSymbolOfNode(node)) && sym->getSymbolEnum() == SymbolEnum::TYPE)   return (CWJType*)sym;

            for(auto s : node->getChilds()){
                if((type = this->getType(s)))   return type;
            }
            return nullptr;
        }

        void AnnotatedTree::getIdent(ResASTNodePtr node,string& str){
            if(node->getNodeType() == NodeType::TOKEN){
                const string& val = node->getContext();
                if(val == "IDENTIFIER")   {
                    str = node->getVal();
//                    cout<<node->getVal()<<endl;
                    return ;
                }
            }
            for(auto s : node->getChilds()){
                this->getIdent(s,str);
                if(str.size())   return;
            }

            return ;

        }

        Variable *AnnotatedTree::lookupVar(AnnotatedTree::ResASTNodePtr ctx, string name) {
            auto& scope = this->scopeOfNodes[ctx];
            while(scope) {
                for (Symbol *t : scope->getSymbols()) {

                    if (t->getSymbolEnum() == SymbolEnum::VARIABLE) {
                        Variable* tmp = (Variable*)t;

                        if(tmp->getName() == name) {
                            return tmp;
                        }
                    }
                }
                scope = scope->getParentScope();
            }
            return nullptr;
        }

        FunctionType *AnnotatedTree::lookupFunction(AnnotatedTree::ResASTNodePtr ctx, const string& name,const vector<CWJType*>& list) {

            auto& scope = this->scopeOfNodes[ctx];
            while(scope) {
                for (Symbol *t : scope->getSymbols()) {

                    if (t->getSymbolEnum() == SymbolEnum::TYPE && ((CWJType*)t)->getUid() == TypeEnum::FUNCTION) {
                        FunctionType *tmp = (FunctionType*)t;
                            // 目前暂时不支持函数重名
//                        if(tmp->getName() == name && tmp->match(list) ) {
//                            return tmp;
//                        }
                            if(tmp->getName() == name){
                                return tmp;
                            }
                    }
                }
                scope = scope->getParentScope();
            }
            return nullptr;
        }


        ClassType::ClassType(const string &name,
                             Scope *enclosingScope,
                             const ResASTNodePtr ctx
                                )
                             :
                             CWJType(name, enclosingScope, ctx, TypeEnum::CLASS) {}

        ClassType::ClassType(Scope *scope,
                             CWJType::ResASTNodePtr const ctx)
                             :
                             CWJType("",scope,ctx,TypeEnum::CLASS) {

            if(!ctx || ctx->getChilds().size() <2 || !ctx->getChilds()[1])  throw runtime_error("ClassType::ClassType error : ctx has not name");

            this->name = ctx->getChilds()[1]->getVal();

        }

        Variable::Variable(const string &name, Scope *enclosingScope,
                           const ResASTNodePtr ctx, CWJType *type, const CwjAny &val,bool isValid ) : Symbol(SymbolEnum::VARIABLE, name,
                                                                                               enclosingScope, ctx),
                                                                                        type(type), val(val),isValid(isValid) {}

        CWJType *const Variable::getType() const {
            return type;
        }

        const CwjAny &Variable::getVal() const {
            return val;
        }

        void Variable::setVal(const CwjAny &val) {
            Variable::val = val;
            isValid = true;
        }

        void Variable::setType(CWJType *type) {
            Variable::type = type;
        }

        bool Variable::isValid1() const {
            return isValid;
        }

        void Variable::setIsValid(bool isValid) {
            Variable::isValid = isValid;
        }

        void Variable::assgin(Variable* var) {
            if(var->type == this->type){
                throw runtime_error("Variable::assgin : type is diff");
            }
            this->setVal(var->getVal());
        }

        ostream &operator<<(ostream &os, const Variable &variable) {
            if(variable.type == &CWJType::intType){
                os <<variable.getVal().cast_<int>();
            }else if(variable.type == &CWJType::stringType){
                os << variable.getVal().cast_<string>();
            }
            return os;
        }

        FunctionType *FunctionScope::getObject() const {
            return object;
        }

        FunctionScope::FunctionScope(Scope::ResASTNodePtr const ctx, Scope *parentScope, FunctionType *obj):Scope(ctx,parentScope),object(obj) {
            this->type = ScopeType::FUNCTION;
        }

        ClassType *ClassScope::getObject() const {
            return object;
        }

        ClassScope::ClassScope(Scope::ResASTNodePtr const ctx, Scope *parentScope, ClassType *obj):Scope(ctx,parentScope),object(obj) {
            this->type = ScopeType::CLASS;
        }

//
//        FunctionObj::FunctionObj(const string &name, Scope *enclosingScope,
//                                 const ResASTNode *ctx, const FunctionType &type) : Symbol(SymbolEnum::FUNCTIONOBJ, name,
//                                                                                           enclosingScope, ctx),
//                                                                                    type(type) {}
        Literal::Literal(Scope *enclosingScope, const ResASTNodePtr ctx,
                          CWJType *type,const string& val) : Symbol(SymbolEnum::LITERAL, Literal::prefix+to_string(Literal::uid), enclosingScope, ctx), type(type), val(val){}


    }
}